package dj.common.lang.utils;

import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * <br>
 * <p>
 * - long id generator
 * </p>
 *
 * <br>
 *
 * @author ZJG
 * @version 1.0
 * @date 2021/12/30 上午11:40
 */
@Slf4j
public final class LongIdGenerator {

    private static Snowflake snowflake = new Snowflake(31L);


    public static String stringId() {
        return String.valueOf(snowflake.nextId());
    }

    public static long id() {
        return snowflake.nextId();
    }

    public static List<String> stringIdList(int quantity) {
        return Stream.iterate(1, i -> i + 1)
                .limit(quantity)
                .map(i -> String.valueOf(snowflake.nextId()))
                .collect(Collectors.toList());
    }

    static class Snowflake {
        private final long bitNum = 62L;
        private final long workerIdBits = 7L;
        private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
        private final long sequenceBits = 12L;
        private final long workerIdShift = sequenceBits;
        private final long timestampLeftShift = sequenceBits + workerIdBits;
        private final long sequenceMask = -1L ^ (-1L << sequenceBits);
        private long workerId;
        private long sequence = 0L;
        private long lastTimestamp = -1L;

        /**
         * 构造函数
         * @param workerId 工作ID (0~127)
         */
        public Snowflake(long workerId) {
            if (workerId > maxWorkerId || workerId < 0) {
                throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
            }
            this.workerId = workerId;
        }

        // ==============================Methods==========================================
        /**
         * 获得下一个ID (该方法是线程安全的)
         * @return SnowflakeId
         */
        public synchronized long nextId() {
            long timestamp = timeGen();
            //如果当前时间小于上一次ID生成的时间戳，说明系统时钟回退过这个时候应当抛出异常
            if (timestamp < lastTimestamp) {
                throw new RuntimeException(
                        String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
            }

            //如果是同一时间生成的，则进行毫秒内序列
            if (lastTimestamp == timestamp) {
                sequence = (sequence + 1) & sequenceMask;
                //毫秒内序列溢出
                if (sequence == 0) {
                    //阻塞到下一个毫秒,获得新的时间戳
                    timestamp = tilNextMillis(lastTimestamp);
                }
            } else {
                //时间戳改变，毫秒内序列重置
                sequence = 0L;
            }
            //上次生成ID的时间截
            lastTimestamp = timestamp;

            return (timestamp << timestampLeftShift) //
                    | (workerId << workerIdShift) //
                    | sequence
                    | 1L << bitNum ;
        }

        /**
         * 阻塞到下一个毫秒，直到获得新的时间戳
         * @param lastTimestamp 上次生成ID的时间截
         * @return 当前时间戳
         */
        protected long tilNextMillis(long lastTimestamp) {
            long timestamp = timeGen();
            while (timestamp <= lastTimestamp) {
                timestamp = timeGen();
            }
            return timestamp;
        }

        /**
         * 返回以毫秒为单位的当前时间
         * @return 当前时间(毫秒)
         */
        protected long timeGen() {
            return System.currentTimeMillis();
        }


    }
}
